package de.ubschmidt.capi;

import de.ubschmidt.capi.util.ByteBufferFactory;
import java.nio.ByteBuffer;
import org.capi.capi20.CapiException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@code CapiPassiveMessage} is the super class for all messages which can
 * be received from the CAPI interface, i.e. confirmation and indication
 * messages.
 */
public abstract class CapiPassiveMessage
        extends CapiMessage {

    static final Logger logger =
            LoggerFactory.getLogger(CapiPassiveMessage.class);

    private ByteBuffer _byteBuffer;

    /** {@inheritDoc} */
    public ByteBuffer getByteBuffer() {
        return _byteBuffer;
    }

    /** {@inheritDoc} */
    public short getTotalLength() {
        return _byteBuffer.getShort(0);
    }

    /** {@inheritDoc} */
    public short getAppId() {
        return _byteBuffer.getShort(2);
    }

    /** {@inheritDoc} */
    public Command getCommand() {
        return Command.valueOf(_byteBuffer.get(4), _byteBuffer.get(5));
    }

    /** {@inheritDoc} */
    public short getMessageNumber() {
        return _byteBuffer.getShort(6);
    }

    /**
     *
     * @param byteBuffer
     */
    public CapiPassiveMessage(final ByteBuffer byteBuffer) {
        _byteBuffer = byteBuffer;
    }

    /**
     * Turns a CAPI message given by a raw array of bytes into a properly
     * typed {@code CapiMessage}.
     *
     * @param buf The CAPI message as a raw array of bytes.
     * @return A typed {@code CapiMessage} which represents the given CAPI
     *         message.
     *
     * @throws CapiException A {@code CapiException} is thrown, if the CAPI
     *         message cannot be decoded.
     */
    public static CapiMessage decode(final byte [] buf)
            throws CapiException {
        ByteBuffer byteBuffer = ByteBufferFactory.wrap(buf);

        byte commandByte = byteBuffer.get(4);
        byte subCommandByte = byteBuffer.get(5);
        Command command = Command.valueOf(commandByte, subCommandByte);

        CapiMessage msg;

        switch (command) {
        case CONNECT_CONF:
            msg = new ConnectConf(byteBuffer); break;
        case LISTEN_CONF:
            msg = new ListenConf(byteBuffer); break;
        case CONNECT_IND:
            msg = new ConnectInd(byteBuffer); break;
        case INFO_IND:
            msg = new InfoInd(byteBuffer); break;
        case DISCONNECT_IND:
            msg = new DisconnectInd(byteBuffer); break;
        case ALERT_CONF:
            msg = new AlertConf(byteBuffer); break;
        case CONNECT_ACTIVE_IND:
            msg = new ConnectActiveInd(byteBuffer); break;
        case CONNECT_B3_IND:
            msg = new ConnectB3Ind(byteBuffer); break;
        case CONNECT_B3_ACTIVE_IND:
            msg = new ConnectB3ActiveInd(byteBuffer); break;
        case DATA_B3_IND:
            msg = new DataB3Ind(byteBuffer); break;
        case DISCONNECT_B3_IND:
            msg = new DisconnectB3Ind(byteBuffer); break;

        default: throw new CapiException((short) 0xffff,
                "unknown command " + command);
        }

        logger.debug("decode(): msg = {}", msg);

        return msg;
    }

}
